using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using OpenWaves.Collections;

namespace OpenWaves.Web.Controls
{
    [ParseChildren(true)]
    [PersistChildren(false)]
    public class Repeater : System.Web.UI.WebControls.Repeater
    {
        public int GroupSize = 1;

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate Template { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate GroupTemplate { get; set; }

        [TemplateContainer(typeof(RepeaterItem))]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public virtual ITemplate FirstItemTemplate { get; set; }

        private readonly PlaceHolder noResultsTemplate = new PlaceHolder();
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public PlaceHolder NoResultsTemplate
        {
            get { return this.noResultsTemplate; }
        }

        private ArrayList itemsArray;
        private RepeaterItemCollection itemsCollection;

        public override RepeaterItemCollection Items
        {
            get
            {
                if (this.itemsCollection == null)
                {
                    if (this.itemsArray == null)
                    {
                        this.EnsureChildControls();
                    }
                    this.itemsCollection = new RepeaterItemCollection(this.itemsArray);
                }
                return this.itemsCollection;
            }
        }

        protected override void InitializeItem(RepeaterItem item)
        {
            if (item.ItemIndex == 0 && this.FirstItemTemplate != null &&
                (item.ItemType == ListItemType.Item || item.ItemType == ListItemType.AlternatingItem))
                this.FirstItemTemplate.InstantiateIn(item);
            else
                base.InitializeItem(item);
        }

        protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType)
        {
            return new RepeaterItem(itemIndex, itemType);
        }

        private RepeaterItem CreateItem(Control placeHolder, int itemIndex, ListItemType itemType, bool dataBind, object dataItem)
        {
            RepeaterItem item = this.CreateItem(itemIndex, itemType);
            RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);
            this.InitializeItem(item);
            if (dataBind)
            {
                item.DataItem = dataItem;
            }
            this.OnItemCreated(e);
            placeHolder.Controls.Add(item);
            if (dataBind)
            {
                item.DataBind();
                this.OnItemDataBound(e);
                item.DataItem = null;
            }
            return item;
        }

        protected override void CreateControlHierarchy(bool useDataSource)
        {
            IEnumerable data = null;
            int dataItemCount = -1;

            this.EnsureEmptyItemsArray();

            if (!useDataSource)
            {
                dataItemCount = (int)this.ViewState["_!ItemCount"];
                if (dataItemCount != -1)
                {
                    data = Enumerable.Repeat(0, dataItemCount);
                    this.itemsArray.Capacity = dataItemCount;
                }
            }
            else
            {
                data = this.GetData();
                ICollection is2 = data as ICollection;
                if (is2 != null)
                {
                    this.itemsArray.Capacity = is2.Count;
                }
            }

            if (data == null || data.Cast<object>().IsEmpty())
            {
                this.Controls.Add(this.NoResultsTemplate);
                this.NoResultsTemplate.DataBind();
            }
            else
            {
                dataItemCount = this.CreateItems(useDataSource, data);
            }

            if (useDataSource)
            {
                this.ViewState["_!ItemCount"] = (data != null) ? dataItemCount : -1;
            }
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        private int CreateItems(bool useDataSource, IEnumerable data)
        {
            Control itemsPlaceholder = this;

            if (this.Template != null)
            {
                PlaceHolder placeHolder = new PlaceHolder();
                this.Template.InstantiateIn(placeHolder);

                this.Controls.Add(placeHolder);

                if(useDataSource)
                {
                    placeHolder.DataBind();
                }

                itemsPlaceholder = placeHolder.GetFirstChildOfTypeOrDefault<ItemsPlaceHolder>();
                Contract.Assert(itemsPlaceholder != null, string.Format(CultureInfo.InvariantCulture,
                    "ItemsPlaceHolder is missing in the Template of {0} repeater.", this.UniqueID));
            }

            if (this.HeaderTemplate != null)
            {
                this.CreateItem(itemsPlaceholder, -1, ListItemType.Header, useDataSource, null);
            }

            int dataItemCount;
            if (this.GroupTemplate != null && this.GroupSize > 1)
            {
                dataItemCount = CreateGroupedItems(data, itemsPlaceholder, useDataSource);
            }
            else
            {
                dataItemCount = this.CreateItems(data, itemsPlaceholder, useDataSource);
            }

            if (this.FooterTemplate != null)
            {
                this.CreateItem(itemsPlaceholder, -1, ListItemType.Footer, useDataSource, null);
            }

            return dataItemCount;
        }

        private int CreateGroupedItems(IEnumerable data, Control itemsPlaceholder, bool useDataSource)
        {
            int itemIndex = 0;
            var dataItemCount = 0;
            var currentGroupItems = new List<object>();
            foreach (object obj2 in data)
            {
                currentGroupItems.Add(obj2);

                if ((itemIndex + 1) % GroupSize == 0 && itemIndex > 0)
                {
                    CreateGroup(currentGroupItems, itemsPlaceholder, useDataSource);
                }

                dataItemCount++;
                itemIndex++;
            }

            // handle remaining items (last group)
            if (currentGroupItems.Count > 0)
            {
                CreateGroup(currentGroupItems, itemsPlaceholder, useDataSource);
            }

            return dataItemCount;
        }

        private int CreateItems(IEnumerable data, Control itemsPlaceholder, bool useDataSource)
        {
            var dataItemCount = 0;
            var itemIndex = 0;
            foreach (var obj2 in data)
            {
                if (this.SeparatorTemplate != null && (dataItemCount > 0))
                {
                    this.CreateItem(itemsPlaceholder, itemIndex - 1, ListItemType.Separator, useDataSource, null);
                }

                ListItemType itemType = ((itemIndex % 2) == 0) ? ListItemType.Item : ListItemType.AlternatingItem;
                RepeaterItem item = this.CreateItem(itemsPlaceholder, itemIndex, itemType, useDataSource, obj2);
                this.itemsArray.Add(item);
                dataItemCount++;
                itemIndex++;
            }
            return dataItemCount;
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        private void CreateGroup(ICollection<object> currentGroupItems, Control itemsPlaceholder, bool useDataSource)
        {
            var group = new RepeaterItemsGroup();
            this.GroupTemplate.InstantiateIn(group);
            itemsPlaceholder.Controls.Add(group);
            group.DataBind();

            var groupItemsPlaceholder = group.GetFirstChildOfTypeOrDefault<ItemsPlaceHolder>();
            Contract.Assert(groupItemsPlaceholder != null, string.Format(CultureInfo.InvariantCulture, 
                "ItemsPlaceHolder is missing in the GroupTemplate of {0} repeater.", this.UniqueID));

            this.CreateItems(currentGroupItems, groupItemsPlaceholder, useDataSource);

            currentGroupItems.Clear();
        }

        private void EnsureEmptyItemsArray()
        {
            if (this.itemsArray != null)
            {
                this.itemsArray.Clear();
            }
            else
            {
                this.itemsArray = new ArrayList();
            }
        }
    }

    /// <summary>
    /// Supports stronly type access to reapated items
    /// </summary>
    /// Template for derived class
    /// 
    /// [TemplateContainer(typeof(RepeaterItem<User>))]        
    /// public new ITemplate ItemTemplate        
    /// {            
    ///     get { return base.ItemTemplate; }
    ///     set { base.ItemTemplate = value; }        
    /// }
    /// <typeparam name="TItem"></typeparam>
    [Browsable(false)]
    public abstract class Repeater<TItem> : Repeater
    {
        protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType)
        {
            return new RepeaterItem<TItem>(itemIndex, itemType);
        }
    }
}